#include "WanP2PClient.h"


namespace cm_nat
{
    WanP2PClient::WanP2PClient()
        :m_mediaSessioin(NULL),
        m_activeLink(NULL),
        m_offerLink(NULL),
        m_selectLink(NULL),
        m_listener(NULL),
        m_activeLinkResult(LINK_CHECK_NULL),
        m_peerLinkResult(LINK_CHECK_NULL),
        m_pingResult(PING_NULL)
    {

    }

    WanP2PClient::~WanP2PClient()
    {

    }

    void WanP2PClient::registerListener( WanP2PClientListener* p_listener )
    {
        m_listener = p_listener;
    }

    int WanP2PClient::setup(std::string username, std::string pwd, std::string server, UInt16 serverPort)
    {
        int res = CM_RES_FAIL;

        m_username = username;
        m_resource = "client";
        m_password = pwd;
        m_server = server;
        m_serverPort = serverPort;

        m_xmppClient.registerListener( this );
        m_xmppClient.setup( m_username , m_password , m_resource, m_server, m_serverPort );
        res = m_xmppClient.connect();
        if( res != CM_RES_SUCCESS ){
            goto fail;
        }

        //m_jingleManager.registerListener( this );
        //m_jingleManager.setup( m_xmppClient.getClientBase() );

        //m_linkPool.setHostIP( IPAddress("192.168.0.128") );
        
        //m_linkPool.setup(2, SocketAddress("stunserver.org", 3478) );

        return CM_RES_SUCCESS;
fail:
        poco_debug(g_log_nat,"WanP2PClient::setup() fail");
        return CM_RES_FAIL;
    }

    int WanP2PClient::teardown()
    {

        return CM_RES_SUCCESS;
    }

    int WanP2PClient::createSession()
    {
        JID jid;
        jid.setUsername( m_username );
        jid.setServer( m_server );
        jid.setResource( m_resource );

        m_mediaSessioin = new MediaSession;
        m_mediaSessioin->setJID( jid );
        m_mediaSessioin->setSessionID( MediaSession::generateSid() );
        m_mediaSessioin->registerLisener( this );

        m_jingleManager.addJingleSession( m_mediaSessioin );
        
        JID peerJid;
        peerJid.setUsername(m_username);
        peerJid.setServer(m_server);
        peerJid.setResource("daemon");
        m_mediaSessioin->setPeerJID( peerJid );

        //send session-initiate to daemon
        m_mediaSessioin->sendSessionInitiate();
        
        return CM_RES_SUCCESS;
    }

    int  WanP2PClient::sendCandidate()
    {
        if( !m_offerLink ){
            m_offerLink = m_linkPool.allocateLink();
        }

        if( m_offerLink ){
            m_mediaSessioin->sendCandidate( m_offerLink->getStunReflexAddress() );
        }

        return CM_RES_SUCCESS;
    }

    int WanP2PClient::selectCandidate()
    {
        if( m_activeLinkResult == LINK_CHECK_SUCCESS ){
            m_selectLink = m_activeLink;
            m_activeLink = NULL;     
        }else if(m_peerLinkResult == LINK_CHECK_SUCCESS ){
            m_selectLink = m_offerLink;
        }
        if( m_selectLink )
            m_mediaSessioin->sendSelectCandidate( true, m_selectLink->getPeerReflexAddress(), m_selectLink->getPeerAddress() );

        return CM_RES_SUCCESS;
    }


    int WanP2PClient::send(ByteBuffer* data)
    {
        if( m_selectLink ){
            m_selectLink->send( data );
        }

        return CM_RES_SUCCESS;
    }

    int WanP2PClient::xmppPing()
    {
        JID peerJid;
        peerJid.setUsername(m_username);
        peerJid.setServer(m_server);
        peerJid.setResource("daemon");

        m_pingResult = PING_NULL;
        m_xmppClient.getClientBase()->xmppPing( peerJid, this );

        return CM_RES_SUCCESS;
    }


    //from JingleManagerListener
    void WanP2PClient::onSessionInitiate(JingleSession* p_js)
    {
        //do nothing in client side
    }

    void WanP2PClient::onSessionTerminate(JingleSession* p_js)
    {
        std::cout<<"onSessionTerminate\n";
    }


    //from MediaSessionListener
    void WanP2PClient::onInitiate( MediaSession* p_session )
    {
        p_session->sendSessionAccept();
        //offer controlled side candidate
        sendCandidate();
    }

    void WanP2PClient::onAccept( MediaSession* p_session )
    {
        //offer controlling side candidate
        sendCandidate();
    }

    void  WanP2PClient::onCandidate( MediaSession* p_session, Candidate* cand )
    {
        SocketAddress peer( cand->m_rel_addr, cand->m_rel_port );

        if( !m_activeLink ){
            m_activeLink = m_linkPool.allocateLink();
            m_activeLink->registerListener( this );
        }
        m_activeLink->checkConnection( peer );
    }


    void WanP2PClient::onCheckResult( MediaSession* p_session, int result )
    {
        poco_debug(g_log_nat,"WanP2PClient::onPeerCheckResult()");

        if( result == MediaSession::CHECK_RES_SUCCESS ){
            m_peerLinkResult = LINK_CHECK_SUCCESS;
        }else{
            m_peerLinkResult = LINK_CHECK_FAIL;
        }
    }

    void WanP2PClient::onSelectCandidate( MediaSession* p_session, Candidate* cand )
    {
        ////do nothing in client side
    }

    //from IceLinkListener
    void WanP2PClient::onReceive(IceLink* link, ByteBuffer* p_data)
    {
        poco_debug(g_log_nat,"WanP2PClient::onReceive");

        if( m_listener ){
            m_listener->onReceive( this, p_data );
        }else{
            SocketAddress* p_sa = (SocketAddress*)p_data->getData();
            delete p_sa;
            p_data->decreaseRef();
        }
    }

    void WanP2PClient::onLinkState( IceLink* link, int state )
    {   
        if( link == m_activeLink ){
            if(state == IceLink::STATE_KEEP_PEER_CHECK ){
                m_activeLinkResult = LINK_CHECK_SUCCESS;
            }
        }
        poco_debug(g_log_nat,"WanP2PClient::onState() active link check connection success");
    }

    //from XMPPClientListener
    void WanP2PClient::onConnect( XMPPClient* xc )
    {
        //ping daemon
        xmppPing();
    }

    void WanP2PClient::onDisconnect( XMPPClient* xc )
    {

    }

    //from EventHandler
    void WanP2PClient::handleEvent( const cm_xmpp::Event& event )
    {
        switch( event.eventType() )
        {
        case cm_xmpp::Event::PingPong:
            {
                m_pingResult = PING_PONG;
                break;
            }
        case cm_xmpp::Event::PingError:
        default:
            {
                m_pingResult = PING_ERROR;
            }
        }
        poco_debug(g_log_nat, " WanP2PClient::handleEvent()");
    }

}